package com.yang.moyu.meta.framework.shiro.service.impl;

import com.yang.moyu.meta.config.properties.JwtProperties;
import com.yang.moyu.meta.framework.constant.CommonRedisKey;
import com.yang.moyu.meta.framework.domain.ClientInfo;
import com.yang.moyu.meta.framework.shiro.jwt.JwtToken;
import com.yang.moyu.meta.framework.shiro.service.LoginRedisService;
import com.yang.moyu.meta.framework.shiro.vo.JwtTokenRedisVo;
import com.yang.moyu.meta.framework.shiro.vo.LoginSysUserRedisVo;
import com.yang.moyu.meta.framework.shiro.vo.LoginSysUserVo;
import com.yang.moyu.meta.framework.util.ClientInfoUtil;
import com.yang.moyu.meta.framework.util.HttpServletRequestUtil;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.List;
import java.util.Set;

/**
 * Create by 10296 on 2021/12/16
 *
 * @Description: 登录信息Redis缓存服务类
 */
@Service
public class LoginRedisServiceImpl implements LoginRedisService {


    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private JwtProperties jwtProperties;


    /**
     * key-value:有过期时间-->token过期
     * 1. tokenMd5：
     * 2.username：
     * 3.username:salt
     * hash：没有过期时间，统计在线的用户信息
     *
     * @param jwtToken
     * @param loginSysUserVo
     */
    @Override
    public void cacheLoginInfo(JwtToken jwtToken, LoginSysUserVo loginSysUserVo) {

        if (jwtToken == null) {
            throw new IllegalArgumentException("jwtToken不能为空");
        }
        if (loginSysUserVo == null) {
            throw new IllegalArgumentException("loginSysUserVo不能为空");
        }
        //token
        String token = jwtToken.getToken();
        //盐值
        String salt = jwtToken.getSalt();
        //登录用户名称
        String username = loginSysUserVo.getUsername();
        //token Md5值
        String tokenMd5 = DigestUtils.md5Hex(token);

        //Redis 缓存JWT Token信息
        JwtTokenRedisVo jwtTokenRedisVo = new JwtTokenRedisVo();
        jwtTokenRedisVo.setHost(jwtToken.getHost());
        jwtTokenRedisVo.setUsername(jwtToken.getUsername());
        jwtTokenRedisVo.setSalt(jwtToken.getSalt());
        jwtTokenRedisVo.setToken(jwtToken.getToken());
        jwtTokenRedisVo.setCreateDate(jwtToken.getCreateDate());
        jwtTokenRedisVo.setExpireDate(jwtToken.getExpireDate());
        jwtTokenRedisVo.setExpireSecond(jwtToken.getExpireSecond());
        //用户客户端信息
        ClientInfo clientInfo = ClientInfoUtil.get(HttpServletRequestUtil.getServletRequest());
        /*Redis 缓存登录信息
         * i将LoginSysUserVo对象赋值到LoginSysUserRedisVo中
         * */
        LoginSysUserRedisVo userRedisVo = new LoginSysUserRedisVo();
        userRedisVo.setUsername(loginSysUserVo.getUsername());
        userRedisVo.setState(loginSysUserVo.getState());
        userRedisVo.setRoleName(loginSysUserVo.getRoleName());
        userRedisVo.setRoleId(loginSysUserVo.getRoleId());
        userRedisVo.setRoleCode(loginSysUserVo.getRoleCode());
        userRedisVo.setPermissionCodes(loginSysUserVo.getPermissionCodes());
        userRedisVo.setNickname(loginSysUserVo.getNickname());
        userRedisVo.setId(loginSysUserVo.getId());
        userRedisVo.setGender(loginSysUserVo.getGender());
        userRedisVo.setDepartmentName(loginSysUserVo.getDepartmentName());
        userRedisVo.setDepartmentId(loginSysUserVo.getDepartmentId());
        userRedisVo.setClientInfo(clientInfo);
        userRedisVo.setSalt(salt);
        /*Redis过期时间与JwtToken过期时间一致*/
        Duration expireDuration = Duration.ofSeconds(jwtToken.getExpireSecond());
        // 判断是否启用单个用户登录，如果是，这每个用户只有一个有效token
        boolean singleLogin = jwtProperties.isSingleLogin();
        if (singleLogin) {
            deleteUserAllCache(username);
        }
        String loginTokenRedisKey = String.format(CommonRedisKey.LOGIN_TOKEN, tokenMd5);
        /*1. tokenMd5 jwtTokenRedisVo*/
        redisTemplate.opsForValue().set(loginTokenRedisKey, jwtTokenRedisVo, expireDuration);
        /*2. username：*/
        redisTemplate.opsForValue().set(String.format(CommonRedisKey.LOGIN_USER, username), userRedisVo, expireDuration);
        // 3. salt hash,方便获取盐值鉴权
        redisTemplate.opsForValue().set(String.format(CommonRedisKey.LOGIN_SALT, username), salt, expireDuration);
        // 4. login user token
        redisTemplate.opsForValue().set(String.format(CommonRedisKey.LOGIN_USER_TOKEN, username, tokenMd5), loginTokenRedisKey, expireDuration);


    }

    @Override
    public void refreshLoginInfo(String oldToken, String username, JwtToken newJwtToken) {
        // 获取缓存的登录用户信息
        LoginSysUserRedisVo loginSysUserRedisVo = getLoginSysUserRedisVo(username);
        // 删除之前的token信息
        deleteLoginInfo(oldToken, username);
        // 缓存登录信息
        cacheLoginInfo(newJwtToken, loginSysUserRedisVo);
    }

    @Override
    public LoginSysUserRedisVo getLoginSysUserRedisVo(String username) {
        if (StringUtils.isBlank(username)) {
            throw new IllegalArgumentException("username不能为空");
        }
        return (LoginSysUserRedisVo) redisTemplate.opsForValue().get(String.format(CommonRedisKey.LOGIN_USER, username));

    }



    @Override
    public LoginSysUserVo getLoginSysUserRedisVoByToken(String token) {
        //获取token md5 值
        String tokenMd5Hex = DigestUtils.md5Hex(token);

        JwtToken jwtToken = (JwtToken) redisTemplate.opsForValue().get(String.format(CommonRedisKey.LOGIN_TOKEN, tokenMd5Hex));
        assert jwtToken != null;
        String username = jwtToken.getUsername();
        LoginSysUserRedisVo loginSysUserRedisVo = (LoginSysUserRedisVo) redisTemplate.opsForValue().get(String.format(CommonRedisKey.LOGIN_USER, tokenMd5Hex));
        //对象转换
        LoginSysUserVo loginSysUserVo = new LoginSysUserVo();

        assert loginSysUserRedisVo != null;
        loginSysUserVo.setUsername(loginSysUserRedisVo.getUsername());
        loginSysUserVo.setDepartmentId(loginSysUserRedisVo.getDepartmentId());
        loginSysUserVo.setDepartmentName(loginSysUserRedisVo.getDepartmentName());
        loginSysUserVo.setGender(loginSysUserRedisVo.getGender());
        loginSysUserVo.setId(loginSysUserRedisVo.getId());
        loginSysUserVo.setNickname(loginSysUserRedisVo.getNickname());
        loginSysUserVo.setPermissionCodes(loginSysUserRedisVo.getPermissionCodes());
        loginSysUserVo.setRoleCode(loginSysUserRedisVo.getRoleCode());
        loginSysUserVo.setRoleId(loginSysUserRedisVo.getRoleId());
        loginSysUserVo.setState(loginSysUserRedisVo.getState());
        loginSysUserVo.setRoleName(loginSysUserRedisVo.getRoleName());
        return loginSysUserVo;

    }


    @Override
    public LoginSysUserVo getLoginSysUserVo(String username) {
        if (StringUtils.isBlank(username)) {
            throw new IllegalArgumentException("username不能为空");
        }
        LoginSysUserRedisVo userRedisVo = getLoginSysUserRedisVo(username);
        return userRedisVo;
    }

    @Override
    public String getSalt(String username) {

        if (StringUtils.isBlank(username)) {
            throw new IllegalArgumentException("username不能为空");
        }
        String salt = (String) redisTemplate.opsForValue().get(String.format(CommonRedisKey.LOGIN_SALT, username));
        return salt;
    }

    @Override
    public void deleteLoginInfo(String token, String username) {
        if (token == null) {
            throw new IllegalArgumentException("token不能为空");
        }
        if (username == null) {
            throw new IllegalArgumentException("username不能为空");
        }
        String tokenMd5 = DigestUtils.md5Hex(token);
        // 1. delete tokenMd5
        redisTemplate.delete(String.format(CommonRedisKey.LOGIN_TOKEN, tokenMd5));
        // 2. delete username
        redisTemplate.delete(String.format(CommonRedisKey.LOGIN_USER, username));
        // 3. delete salt
        redisTemplate.delete(String.format(CommonRedisKey.LOGIN_SALT, username));
        // 4. delete user token
        redisTemplate.delete(String.format(CommonRedisKey.LOGIN_USER_TOKEN, username, tokenMd5));

    }

    @Override
    public boolean exists(String token) {
        if (token == null) {
            throw new IllegalArgumentException("token不能为空");
        }
        String tokenMd5 = DigestUtils.md5Hex(token);
        Object object = redisTemplate.opsForValue().get(String.format(CommonRedisKey.LOGIN_TOKEN, tokenMd5));
        return object != null;
    }

    @Override
    public void deleteUserAllCache(String username) {
        Set<String> userTokenMd5Set = redisTemplate.keys(String.format(CommonRedisKey.LOGIN_USER_ALL_TOKEN, username));
        if (CollectionUtils.isEmpty(userTokenMd5Set)) {
            return;
        }

        // 1. 删除登录用户的所有token信息
        List<String> tokenMd5List = redisTemplate.opsForValue().multiGet(userTokenMd5Set);
        redisTemplate.delete(tokenMd5List);
        // 2. 删除登录用户的所有user:token信息
        redisTemplate.delete(userTokenMd5Set);
        // 3. 删除登录用户信息
        redisTemplate.delete(String.format(CommonRedisKey.LOGIN_USER, username));
        // 4. 删除登录用户盐值信息
        redisTemplate.delete(String.format(CommonRedisKey.LOGIN_SALT, username));

    }

}
